using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public abstract class Currency {
	public int amount;
	
	public Currency(int amount = 0) {
		this.amount = amount;
	}
	
	/* TODO: figure out cloning with abstract base classes */
	public static Currency CopyTo(Currency copy_to, Currency copy_from) {
		copy_to.amount = copy_from.amount;
		return copy_to;
	}
	
	public abstract Currency Clone();
	public abstract string GetSymbolString();
}

public class GoldCurrency : Currency {
	public GoldCurrency(int amount=0) : base(amount) { }
	
	public override Currency Clone() {
		return Currency.CopyTo(new GoldCurrency(), this);
	}
	
	public override string GetSymbolString() {
		return "G";
	}
}

public class NyotaCurrency : Currency {
	public NyotaCurrency(int amount=0) : base(amount) { }
	
	public override Currency Clone() {
		return Currency.CopyTo(new NyotaCurrency(), this);
	}
	
	public override string GetSymbolString() {
		return "N";
	}
}

public class PriceCurrency : IEnumerable {
	private Dictionary<System.Type, Currency> currencies = null;
	
	public PriceCurrency(Currency c = null) {
		this.currencies = new Dictionary<System.Type, Currency>();
		
		if (c != null) {
			this.currencies[c.GetType()] = c;
		}
	}
	
	public PriceCurrency(Dictionary<System.Type,Currency> currencies) {
		this.currencies = new Dictionary<System.Type, Currency>();
		
		foreach (KeyValuePair<System.Type, Currency> p in currencies) {
			this.currencies[p.Key] = p.Value.Clone ();
		}
	}
	
	Currency GetCurrencyAt(System.Type t) {
		if (this.currencies.ContainsKey (t)) {
			return this.currencies[t];
		} else {
			return null;
		}		
	}
	
	public int GetAmount(System.Type t) {
		Currency c = this.GetCurrencyAt(t);
		
		if (c == null) {
			return 0;
		} else {
			return c.amount;
		}
	}
	
	/* mutates */
	public PriceCurrency Multiply(int multiplier) {
		foreach (Currency c in this) {
			c.amount = c.amount * multiplier;
		}
		
		return this;
	}
	
	public PriceCurrency Multiply(System.Type t, int multiplier) {
		Currency c = this.GetCurrencyAt(t);
		
		if (c != null) {
			c.amount = c.amount * multiplier;
		}
		
		return this;
	}
	
	public PriceCurrency Clone() {
		return new PriceCurrency(this.currencies);
	}
	
	IEnumerator IEnumerable.GetEnumerator() {
       return (IEnumerator) GetEnumerator();
    }
	
	public CurrencyEnumerator GetEnumerator() {
		return new CurrencyEnumerator(this.currencies.GetEnumerator());
	}
	
	public override string ToString() {
		string s = "";
		bool first = true;
		
		foreach (Currency c in this) {
			
			if (first) {
				first = false;
			} else {
				s += ", ";
			}
			s += c.amount.ToString () + " " + c.ToString ();
		}
		
		return s;
	}
}

/**
 * Wrapper for an iterator of dictionary System.Type -> Currency
 */
public class CurrencyEnumerator : IEnumerator {
	public IEnumerator e;
	
	public CurrencyEnumerator(IEnumerator e) {
		this.e = e;
	}
	
	public bool MoveNext() {
		return this.e.MoveNext();
	}
	
	public void Reset() { 
		this.e.Reset ();
	}
	
	public Currency Current {
		get {
			KeyValuePair<System.Type, Currency> kv = 
				(KeyValuePair<System.Type, Currency>)this.e.Current;
			
			return kv.Value;
		}
	}
	
	object IEnumerator.Current {
		get {
			return this.e.Current;
		}
	}
}

public class AccountCurrency : IEnumerable {
	private Dictionary<System.Type, Currency> currencies = null;
	
	public AccountCurrency() {
		this.currencies = new Dictionary<System.Type, Currency>();
	}
	
	public int GetAmount(System.Type t) {
		if (this.currencies.ContainsKey (t)) {
			return this.currencies[t].amount;
		} else {
			return 0;
		}
	}
	
	public int GetAmount(Currency c) {
		return this.GetAmount (c.GetType ());
	}
	
	private bool ModifyAmount(Currency c, int multiplier) {
		if (!this.currencies.ContainsKey (c.GetType())) {
			Currency empty = c.Clone ();
			
			empty.amount = multiplier * empty.amount;
			
			this.currencies[c.GetType ()] = empty;
		} else {
			this.currencies[c.GetType ()].amount += c.amount * multiplier;
		}
		
		return true;
	}
	
	public bool AddAmount(Currency c) {
		return this.ModifyAmount(c, 1);
	}
	
	public bool SubtractAmount(Currency c) {
		return this.ModifyAmount(c, -1);
	}

	private bool ModifyAmount(PriceCurrency p, int multiplier) {
		foreach (Currency c in p) {
			this.ModifyAmount(c, multiplier);
		}
		
		/* TODO: maybe check to prevent negatives */
		return true;
	}
	
	public bool AddAmount(PriceCurrency p) {
		return this.ModifyAmount(p, 1);
	}
	
	public bool SubtractAmount(PriceCurrency p) {
		return this.ModifyAmount(p, -1);
	}
	
	public bool CanAfford(PriceCurrency p) {
		foreach (Currency c in p) {
			if (!this.CanAfford(c)) {
				return false;
			}
		}
		
		return true;
	}
	
	public bool CanAfford(Currency c) {
		return (this.GetAmount(c) >= c.amount);
	}
	
	IEnumerator IEnumerable.GetEnumerator() {
       return (IEnumerator) GetEnumerator();
    }
	
	public CurrencyEnumerator GetEnumerator() {
		return new CurrencyEnumerator(this.currencies.GetEnumerator());
	}
}
